package com.sukaiyi.giteeideaplugin.setting;

import cn.hutool.core.util.StrUtil;
import com.intellij.icons.AllIcons;
import com.intellij.ide.BrowserUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.SearchableConfigurable;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBRadioButton;
import com.intellij.util.messages.MessageBus;
import com.sukaiyi.giteeideaplugin.externcmd.CommandExecutor;
import com.sukaiyi.giteeideaplugin.utils.ProjectUtils;
import com.sukaiyi.giteejavaapi.common.Gitee;
import com.sukaiyi.giteejavaapi.http.model.HttpRequest;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.util.HashMap;
import java.util.Objects;

/**
 * @author sukaiyi
 */
public class Configurable implements SearchableConfigurable {

    private final PluginSetting pluginSetting = PluginSetting.getInstance();

    private TextFieldWithBrowseButton clonePathField;
    private JPasswordField accessTokenField;
    private JButton testAccessTokenButton;
    private JBLabel accessTokenTestResultLabel;
    private JPanel content;
    private com.intellij.ui.components.labels.LinkLabel<String> gotoGetAccessTokenLabel;
    private JBRadioButton sshRadioButton;
    private JBRadioButton httpsRadioButton;
    private TextFieldWithBrowseButton gitExecutablePathField;
    private JButton testGitButton;
    private JBLabel gitTestResultLabel;

    public Configurable() {
    }

    @NotNull
    @Override
    public String getId() {
        return Configurable.class.getName();
    }

    @Nls
    @Override
    public String getDisplayName() {
        return "Gitee Support";
    }

    @Nullable
    @Override
    public JComponent createComponent() {
        clonePathField.addBrowseFolderListener("Clone Path", null, ProjectUtils.getProject(), FileChooserDescriptorFactory.createSingleFolderDescriptor());
        gitExecutablePathField.addBrowseFolderListener("Select Executable", null, ProjectUtils.getProject(), FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor());
        flushUI();
        accessTokenTestResultLabel.setVisible(false);
        gitTestResultLabel.setVisible(false);
        testAccessTokenButton.addActionListener(this::onTestAccessTokenButton);
        testGitButton.addActionListener(this::onTestGitButton);

        gotoGetAccessTokenLabel.setListener((label, s)->BrowserUtil.browse("https://gitee.com/profile/personal_access_tokens"), null);
        return content;
    }

    private void flushUI() {
        accessTokenField.setText(pluginSetting.getGiteeAccessToken());
        clonePathField.setText(pluginSetting.getDefaultClonePath());
        if (Objects.equals(pluginSetting.getDefaultProtocol(), "https")) {
            httpsRadioButton.setSelected(true);
        } else {
            httpsRadioButton.setSelected(false);
        }
        gitExecutablePathField.setText(pluginSetting.getGitExecutablePath());
    }

    private void onTestAccessTokenButton(ActionEvent event) {
        String accessToken = new String(accessTokenField.getPassword());

        HttpRequest request = new HttpRequest();
        request.setMethod("GET");
        request.setHeaders(new HashMap<String, String>() {{
            put("Connection", "close");
            put("Accept", "application/json, text/plain, */*");
            put("User-Agent", "GiteeIdeaPlugin");
        }});
        request.setUrl(String.format("https://gitee.com/api/v5/user?access_token=%s", accessToken));
        Gitee.getInstance().invoke(request)
                .whenComplete((response, throwable) -> SwingUtilities.invokeLater(() -> {
                    accessTokenTestResultLabel.setVisible(true);
                    if (throwable != null) {
                        accessTokenTestResultLabel.setIcon(AllIcons.RunConfigurations.ToolbarFailed);
                        accessTokenTestResultLabel.setText("Test failed: " + throwable.getMessage());
                        return;
                    }
                    int httpStatus = response.getStatus();
                    if (httpStatus == 200) {
                        String name = response.asJSONObject().getStr("name");
                        accessTokenTestResultLabel.setIcon(AllIcons.RunConfigurations.ToolbarPassed);
                        accessTokenTestResultLabel.setText(String.format("Hello, %s. You have successfully connected to Gitee", name));
                    } else {
                        String message = response.asJSONObject().getStr("message");
                        accessTokenTestResultLabel.setIcon(AllIcons.RunConfigurations.ToolbarFailed);
                        accessTokenTestResultLabel.setText(String.format("Test failed:%s", message));
                    }
                }));
    }

    private void onTestGitButton(ActionEvent event) {
        String gitPath = gitExecutablePathField.getText();
        CommandExecutor.execAsync(String.format("%s --version", gitPath))
                .whenComplete(((commandResult, throwable) -> SwingUtilities.invokeLater(() -> {
                    gitTestResultLabel.setVisible(true);
                    if (throwable != null) {
                        gitTestResultLabel.setIcon(AllIcons.RunConfigurations.ToolbarFailed);
                        gitTestResultLabel.setText("Test failed: " + throwable.getMessage());
                        return;
                    }
                    int exitCode = commandResult.getExitCode();
                    String message = commandResult.getOutput();
                    if (exitCode == 0 && !StrUtil.isEmpty(message) && message.startsWith("git version")) {
                        gitTestResultLabel.setIcon(AllIcons.RunConfigurations.ToolbarPassed);
                        gitTestResultLabel.setText(message);
                        return;
                    }
                    if (exitCode != 0) {
                        gitTestResultLabel.setIcon(AllIcons.RunConfigurations.ToolbarFailed);
                        gitTestResultLabel.setText("Test failed: " + message + ", exitCode: " + exitCode);
                        return;
                    }
                    if (StrUtil.isEmpty(message) || !message.startsWith("git version")) {
                        gitTestResultLabel.setIcon(AllIcons.RunConfigurations.ToolbarFailed);
                        gitTestResultLabel.setText("Test failed: this file doesnt like a git executable file");
                    }
                })));
    }

    @Override
    public boolean isModified() {
        return !Objects.equals(clonePathField.getText(), pluginSetting.getDefaultClonePath()) ||
                !Objects.equals(new String(accessTokenField.getPassword()), pluginSetting.getGiteeAccessToken()) ||
                sshRadioButton.isSelected() && !Objects.equals("ssh", pluginSetting.getDefaultProtocol()) ||
                httpsRadioButton.isSelected() && !Objects.equals("https", pluginSetting.getDefaultProtocol()) ||
                !Objects.equals(gitExecutablePathField.getText(), pluginSetting.getGitExecutablePath());
    }

    @Override
    public void apply() throws ConfigurationException {
        pluginSetting.setDefaultClonePath(clonePathField.getText());
        pluginSetting.setDefaultProtocol(sshRadioButton.isSelected() ? "ssh" : "https");
        pluginSetting.setGitExecutablePath(gitExecutablePathField.getText());

        // 如果 Access Token 发生了变化，广播一个消息
        String accessTokenBefore = pluginSetting.getGiteeAccessToken();
        String accessTokenAfter = new String(accessTokenField.getPassword());
        if (!Objects.equals(accessTokenBefore, accessTokenAfter)) {
            pluginSetting.setGiteeAccessToken(accessTokenAfter);
            MessageBus bus = ApplicationManager.getApplication().getMessageBus();
            AccessTokenChangeNotifier notifier = bus.syncPublisher(AccessTokenChangeNotifier.TOPIC);
            notifier.onChange(accessTokenBefore, accessTokenAfter);
        }

    }
}
